/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.mp.base.BaseEntity;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.utils.DateTimeUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.entity.*;
import org.springblade.modules.admin.enums.MaintainPlanContentEnum;
import org.springblade.modules.admin.enums.TaskStatusEnum;
import org.springblade.modules.admin.mapper.MaintainPlanContentExtMapper;
import org.springblade.modules.admin.service.*;
import org.springblade.modules.admin.vo.AssignmentCountsVO;
import org.springblade.modules.admin.vo.MaintainPlanContentExtVO;
import org.springblade.modules.admin.vo.plan.store.WxSendWeiXinMsgVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *  服务实现类
 *
 * @author ysq
 * @since 2022-03-28
 */
@Service
@Slf4j
public class MaintainPlanContentExtServiceImpl extends BaseServiceImpl<MaintainPlanContentExtMapper, MaintainPlanContentExt> implements IMaintainPlanContentExtService {

	@Autowired
	@Lazy
	private IMaintainPlanContentService maintainPlanContentService;

	@Autowired
	private IBuildingExtService buildingExtService;

	@Autowired
	private ElevatorService elevatorService;

	@Autowired
	@Lazy
	private IMaintainPlanContentUserService maintainPlanContentUserService;
	@Autowired
	private MaintainMalfunctionLogService malfunctionLogService;

	@Override
	public IPage<MaintainPlanContentExtVO> selectMaintainPlanContentExtPage(IPage<MaintainPlanContentExtVO> page, MaintainPlanContentExtVO maintainPlanContentExt) {
		return page.setRecords(baseMapper.selectMaintainPlanContentExtPage(page, maintainPlanContentExt));
	}

	@Override
	public List<MaintainPlanContentExt> getList() {
		return baseMapper.getList();
	}

	@Override
	public List<MaintainPlanContentExt> listAllPlanDisposeSignatureTime() {
		return baseMapper.listAllPlanDisposeSignatureTime();
	}

	@Override
	public List<MaintainPlanContentExtVO> maintainPlanList(Long elevatorId) {
		return baseMapper.maintainPlanList(elevatorId);
	}

	/**
	 * 预留方法，批量补充表数据
	 * @return 是否成功
	 */
	@Override
	@TenantIgnore
	@Transactional(rollbackFor = Exception.class)
	public Boolean init() {
		// 0. 阈值
		boolean isSuccess = true;
		long minutes = 30;
		long door = TimeUnit.MINUTES.toMillis(minutes);
		// 1. 获取所有救援工单列表
		List<MaintainPlanContentExt> extList = baseMapper.getRescuePlanList();
		// 需要补充到达数据的列表
		List<MaintainPlanContentExt> need = extList.stream().filter(f -> Func.isNull(f.getIsOvertime())).collect(Collectors.toList());
		// 2. 救援工单列表到达时间-开始时间 > 30 分钟，该工单是否延迟设置1
		for(MaintainPlanContentExt e : need){
			if(!Func.isNull(e.getArriveTime())){
				long createTime = maintainPlanContentService.getById(e.getPlanId()).getCreateTime().getTime();
				long diff = e.getArriveTime().getTime() - createTime;
				// 计算并设置到达时长
				e.setArriveDuration(diff);
				// 到达时间-开始时间 > 30 分钟，该工单是否延迟设置1
				if(diff > door ){
					e.setIsOvertime(1);
				}
				// 3. 救援工单列表到达时间-开始时间 < 30 分钟，且该项目没有自定义设置时间，该工单是否延迟设置3
				if(diff <= door){
					Long planId = e.getPlanId();
					MaintainPlanContent plan = maintainPlanContentService.getById(planId);
					Long elevatorId = plan.getElevatorId();
					Elevator elevator = elevatorService.getById(elevatorId);
					Long buildingId = elevator.getBuildingId();
					BuildingExt buildingExt = buildingExtService.getOne(new LambdaQueryWrapper<BuildingExt>()
						.eq(BuildingExt::getBuildingId, buildingId)
						.eq(BaseEntity::getIsDeleted, 0)
					);
					Integer rescueTime = buildingExt.getRescueTime();
					// 该项目没有自定义设置时间，该工单是否延迟设置3
					if( Func.isNull(rescueTime) || rescueTime == 0 ){
						e.setIsOvertime(3);
					}else{
						// 4. 救援工单列表到达时间-开始时间 < 30 分钟，且该项目有自定义设置时间 先判断白天或黑夜
						// 创建一个 Calendar 对象，并设置未工单创建时间
						Calendar calendar = Calendar.getInstance();
						calendar.setTime(e.getCreateTime());
						// 获取当前时间的小时
						int hour = calendar.get(Calendar.HOUR_OF_DAY);
						// 判断时间是否在 6:00 ~ 18:00 时间段内
						boolean isWithinTimeRange = hour >= 6 && hour < 18;
						if(isWithinTimeRange){
							Integer rescueDaytime = buildingExt.getRescueDaytime();
							long dayDoor = TimeUnit.MINUTES.toMillis(rescueDaytime);
							if(diff <= dayDoor){
								e.setIsOvertime(3);
							}else{
								e.setIsOvertime(2);
							}
						}else{
							// 5. 白天或黑夜超时，该工单是否延迟设置2，不超时，设置延迟3
							Integer rescueNight = buildingExt.getRescueNight();
							long nightDoor = TimeUnit.MINUTES.toMillis(rescueNight);
							if(diff <= nightDoor){
								e.setIsOvertime(3);
							}else{
								e.setIsOvertime(2);
							}
						}
					}
				}
				boolean b = this.updateById(e);
				isSuccess = isSuccess && b;
			}
		}
		// 4. 补充所有work mill数据与分配时间
		extList.forEach(a ->{
			if(a.getPlanBeginTime() != null && a.getPlanEndTime() != null){
				long beginTime = a.getPlanBeginTime().toInstant(ZoneOffset.UTC).toEpochMilli();
				long endTime = a.getPlanEndTime().toInstant(ZoneOffset.UTC).toEpochMilli();
				long workMill = endTime - beginTime;
				a.setWorkMill(workMill);
			}
		});
		log.info("开始初始化工作时长毫秒值，涉及工单数{}",extList.size());
		// 5. 补充所有分配时间与响应时长
		extList.forEach(a ->{
			Long planId = a.getPlanId();
			MaintainPlanContent currentPlan = maintainPlanContentService.getById(planId);
			Date createTime = currentPlan.getCreateTime();
			// 当分配时间不为空时，直接计算
			if(!Func.isNull(a.getAllocateTime())) {
				long allocateDuration = a.getAllocateTime().getTime() - createTime.getTime();
				a.setAllocateDuration(allocateDuration);
			}else{
				// 当分配时间为空时，拿用户关联表最快分配到的时间，计算，获得allocateTime
				MaintainPlanContentUser theFastUser = baseMapper.getTheFastUser(planId);
				if(!Func.isNull(theFastUser)){
					Date getTime = theFastUser.getCreateTime();
					a.setAllocateTime(getTime);
					long allocateDuration = a.getAllocateTime().getTime() - createTime.getTime();
					a.setAllocateDuration(allocateDuration);
				}
			}
		});
		log.info("即将初始化{}条数据,影响新增的字段:",extList.size());
		boolean isInserted = this.updateBatchById(extList);
		return isSuccess && isInserted ;
	}

	@Override
	public Boolean assignmentCountsScript() {
		List<AssignmentCountsVO> dataList = baseMapper.assignmentCounts();
		List<List<AssignmentCountsVO>> splitDataList = CollUtil.split(dataList, 100);
		for (int i = 0; i < splitDataList.size(); i++) {
			List<AssignmentCountsVO> itemList = splitDataList.get(i).stream().filter(t -> t.getPlanExtId() != null).collect(Collectors.toList());
			List<MaintainPlanContentExt> updateList = itemList.stream().map(t -> {
				MaintainPlanContentExt ext = new MaintainPlanContentExt();
				ext.setId(t.getPlanExtId());
				ext.setAssignmentCounts(t.getElevatorCount());
				return ext;
			}).collect(Collectors.toList());
			this.updateBatchById(updateList);
		}
		return true;
	}

	@Override
	public Boolean initPlanCommit() {
		List<Integer> status = new ArrayList<>();
		status.add(TaskStatusEnum.BY_FINISH.getStatus());
		status.add(TaskStatusEnum.BY_SIGN.getStatus());
		status.add(TaskStatusEnum.BY_MAINTAIN_SIGN.getStatus());
		List<MaintainPlanContent> planContentList = maintainPlanContentService.list(Wrappers.<MaintainPlanContent>lambdaQuery()
			.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_BY.getId()).in(MaintainPlanContent::getStatus,status));
		List<Long> planIds = planContentList.stream().map(MaintainPlanContent::getId).collect(Collectors.toList());
		List<MaintainPlanContentExt> planContentExts = super.list(Wrappers.<MaintainPlanContentExt>lambdaQuery().in(MaintainPlanContentExt::getPlanId,planIds));
		List<MaintainMalfunctionLog> logs = malfunctionLogService.list(Wrappers.<MaintainMalfunctionLog>lambdaQuery().in(MaintainMalfunctionLog::getPlanId,planIds)
			.eq(MaintainMalfunctionLog::getPlanStatus,TaskStatusEnum.BY_FINISH.getStatus()));
		Map<Long,List<MaintainMalfunctionLog>> planGroup = logs.stream().collect(Collectors.groupingBy(MaintainMalfunctionLog::getPlanId));
		Map<Long,Date> planUpdateTimeMap = planContentList.stream().collect(Collectors.toMap(MaintainPlanContent::getId,MaintainPlanContent::getUpdateTime));
		planContentExts.forEach(e -> {
			e.setCommitTime(planUpdateTimeMap.get(e.getPlanId()));
			if(planGroup.containsKey(e.getPlanId())){
				List<MaintainMalfunctionLog> list = planGroup.get(e.getPlanId()).stream().sorted(Comparator.comparing(MaintainMalfunctionLog::getCreateTime)).collect(Collectors.toList());
				if(Func.isNotEmpty(list)){
					e.setCommitTime(list.get(0).getCreateTime());
				}
			}
		});
		List<List<MaintainPlanContentExt>> p = ListUtil.partition(planContentExts,50);
		p.forEach(super::updateBatchById);
		return Boolean.TRUE;
	}

    @Override
    public List<WxSendWeiXinMsgVo> getScoreYesterdayList() {
        return baseMapper.getScoreYesterdayList(DateUtil.yesterday());
    }

	@Override
	public List<MaintainPlanContentExt> listAllUnfinishedByPlan() {
		return baseMapper.listAllUnfinishedByPlan();
	}
}
